// Copyright 2023 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <TestUtils/TiFlashTestBasic.h>

#include <TiDB/Decode/JsonBinary.cpp>

namespace DB
{
namespace tests
{
class TestJsonBinary : public ::testing::Test
{
public:
    static void checkJsonPathKeyCacheDisable(std::shared_ptr<JsonPathExpr> path_expr)
    {
        for (const auto & leg_ptr : path_expr->getLegs())
        {
            if (leg_ptr->type == JsonPathLeg::JsonPathLegKey)
                ASSERT_TRUE(leg_ptr->dot_key.status == JsonPathObjectKeyCacheDisabled);
        }
    }

    static void checkJsonPathKeyUncached(std::shared_ptr<JsonPathExpr> path_expr)
    {
        for (const auto & leg_ptr : path_expr->getLegs())
        {
            if (leg_ptr->type == JsonPathLeg::JsonPathLegKey)
                ASSERT_TRUE(leg_ptr->dot_key.status == JsonPathObjectKeyUncached);
        }
    }
};

TEST_F(TestJsonBinary, TestUnquoteLiteral)
try
{
    char json_literals[3] = {
        0x0, /// null
        0x1, /// true
        0x2 /// false
    };
    std::vector<String> expected = {"null", "true", "false"};
    for (size_t i = 0; i < 3; ++i)
    {
        JsonBinary json_binary(JsonBinary::TYPE_CODE_LITERAL, StringRef(json_literals + i, 1));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected[i]);
    }
}
CATCH

TEST_F(TestJsonBinary, TestUnquoteInteger)
try
{
    UInt8 json_integers[3][8] = {
        {0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
        {0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
        {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
    };
    std::vector<String> expected = {"-3", "3", "18446744073709551615"};
    {
        JsonBinary json_binary(JsonBinary::TYPE_CODE_INT64, StringRef(json_integers[0], 8));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected[0]);
    }
    {
        JsonBinary json_binary(JsonBinary::TYPE_CODE_INT64, StringRef(json_integers[1], 8));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected[1]);
    }
    {
        JsonBinary json_binary(JsonBinary::TYPE_CODE_UINT64, StringRef(json_integers[2], 8));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected[2]);
    }
}
CATCH

TEST_F(TestJsonBinary, TestUnquoteFloat64)
try
{
    UInt8 json_floats[16][8] = {
        {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x40},
        {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0},
        {0x15, 0x1e, 0x9f, 0x79, 0x96, 0x1b, 0x4b, 0x44},
        {0x63, 0x4a, 0x45, 0x1e, 0xa6, 0x14, 0x4b, 0x44},
        {0x46, 0x44, 0xb9, 0xe6, 0x8f, 0xc4, 0xb0, 0x3e},
        {0x76, 0x83, 0xd, 0xf4, 0xf5, 0x21, 0xb4, 0x3e},
        {0xd9, 0x44, 0xb9, 0xbf, 0xb9, 0x89, 0x1a, 0x3f},
        {0x0, 0x80, 0xe0, 0x37, 0x79, 0xc3, 0x41, 0x43},
        {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc0},
        {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80},
        {0x15, 0x1e, 0x9f, 0x79, 0x96, 0x1b, 0x4b, 0xc4},
        {0x63, 0x4a, 0x45, 0x1e, 0xa6, 0x14, 0x4b, 0xc4},
        {0x46, 0x44, 0xb9, 0xe6, 0x8f, 0xc4, 0xb0, 0xbe},
        {0x76, 0x83, 0xd, 0xf4, 0xf5, 0x21, 0xb4, 0xbe},
        {0xd9, 0x44, 0xb9, 0xbf, 0xb9, 0x89, 0x1a, 0xbf},
        {0x0, 0x80, 0xe0, 0x37, 0x79, 0xc3, 0x41, 0xc3},
    };
    std::vector<String> expected = {
        "3",
        "0",
        "1.0001e+21",
        "999100000000000000000",
        "9.9944e-7",
        "1.2e-6",
        "0.0001012344",
        "10000000000000000",
        "-3",
        "-0",
        "-1.0001e+21",
        "-999100000000000000000",
        "-9.9944e-7",
        "-1.2e-6",
        "-0.0001012344",
        "-10000000000000000",
    };
    for (size_t i = 0; i < expected.size(); ++i)
    {
        JsonBinary json_binary(JsonBinary::TYPE_CODE_FLOAT64, StringRef(json_floats[i], 8));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected[i]);
    }
}
CATCH

TEST_F(TestJsonBinary, TestUnquoteObject)
try
{
    {
        UInt8 json_object[] = {0x0, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0};
        JsonBinary json_binary(JsonBinary::TYPE_CODE_OBJECT, StringRef(json_object, 8));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == "{}");
    }

    {
        UInt8 json_object[] = {0x1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0,
                               0x0, 0x1, 0x0, 0xc, 0x14, 0x0, 0x0, 0x0, 0x61, 0x1, 0x62};
        JsonBinary json_binary(
            JsonBinary::TYPE_CODE_OBJECT,
            StringRef(json_object, sizeof(json_object) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == "{\"a\": \"b\"}");
    }

    {
        UInt8 json_object[] = {0x1, 0x0,  0x0, 0x0, 0x1c, 0x0,  0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0,
                               0x9, 0x14, 0x0, 0x0, 0x0,  0x61, 0x3, 0x0, 0x0,  0x0, 0x0, 0x0, 0x0, 0x0};
        JsonBinary json_binary(
            JsonBinary::TYPE_CODE_OBJECT,
            StringRef(json_object, sizeof(json_object) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == "{\"a\": 3}");
    }

    {
        UInt8 json_object[]
            = {0x1, 0x0, 0x0, 0x0, 0x14, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x61};
        JsonBinary json_binary(
            JsonBinary::TYPE_CODE_OBJECT,
            StringRef(json_object, sizeof(json_object) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == "{\"a\": null}");
    }
}
CATCH

TEST_F(TestJsonBinary, TestUnquoteArray)
try
{
    {
        UInt8 json_array[] = {0x0, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0};
        JsonBinary json_binary(JsonBinary::TYPE_CODE_ARRAY, StringRef(json_array, sizeof(json_array) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == "[]");
    }

    {
        UInt8 json_array[] = {0x2, 0x0, 0x0, 0x0,  0x16, 0x0, 0x0, 0x0, 0xc,  0x12, 0x0,
                              0x0, 0x0, 0xc, 0x14, 0x0,  0x0, 0x0, 0x1, 0x61, 0x1,  0x62};
        JsonBinary json_binary(JsonBinary::TYPE_CODE_ARRAY, StringRef(json_array, sizeof(json_array) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == "[\"a\", \"b\"]");
    }

    {
        UInt8 json_array[] = {0x1, 0x0, 0x0, 0x0, 0xd, 0x0, 0x0, 0x0, 0x4, 0x1, 0x0, 0x0, 0x0};
        JsonBinary json_binary(JsonBinary::TYPE_CODE_ARRAY, StringRef(json_array, sizeof(json_array) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == "[true]");
    }

    {
        UInt8 json_array[] = {0x1, 0x0, 0x0, 0x0, 0x15, 0x0, 0x0, 0x0, 0x9, 0xd, 0x0,
                              0x0, 0x0, 0x3, 0x0, 0x0,  0x0, 0x0, 0x0, 0x0, 0x0};
        JsonBinary json_binary(JsonBinary::TYPE_CODE_ARRAY, StringRef(json_array, sizeof(json_array) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == "[3]");
    }
}
CATCH

TEST_F(TestJsonBinary, TestUnquoteMixed)
try
{
    {
        // clang-format off
        UInt8 json_array[] = {
            0x3, 0x0, 0x0, 0x0, 0x31, 0x0, 0x0, 0x0, 0xc, 0x17, 0x0, 0x0, 0x0, 0xc, 0x19, 0x0, 0x0, 0x0, 0x1, 0x1b,
            0x0, 0x0, 0x0, 0x1, 0x61, 0x1, 0x62, 0x1, 0x0, 0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1,
            0x0, 0xc, 0x14, 0x0, 0x0, 0x0, 0x61, 0x1, 0x62
        };
        // clang-format on
        JsonBinary json_binary(JsonBinary::TYPE_CODE_ARRAY, StringRef(json_array, sizeof(json_array) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == "[\"a\", \"b\", {\"a\": \"b\"}]");
    }

    {
        // clang-format off
        UInt8 json_array[] = {
            0x4, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x9, 0x1c, 0x0, 0x0, 0x0, 0xb, 0x24, 0x0, 0x0, 0x0, 0xc, 0x2c, 0x0, 0x0, 0x0, 0x1,
            0x2e, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x40, 0x1, 0x33, 0x1, 0x0,
            0x0, 0x0, 0x16, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0xc, 0x14, 0x0, 0x0, 0x0, 0x61, 0x1, 0x62
        };
        // clang-format on
        JsonBinary json_binary(JsonBinary::TYPE_CODE_ARRAY, StringRef(json_array, sizeof(json_array) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == "[3, 3, \"3\", {\"a\": \"b\"}]");
    }

    {
        // clang-format off
        UInt8 json_array[] = {
            0x2, 0x0, 0x0, 0x0, 0x63, 0x0, 0x0, 0x0, 0xc, 0x12, 0x0, 0x0, 0x0, 0x1, 0x14, 0x0, 0x0, 0x0, 0x1, 0x61, 0x1, 0x0, 0x0, 0x0,
            0x4f, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0x3, 0x14, 0x0, 0x0, 0x0, 0x61, 0x3, 0x0, 0x0, 0x0, 0x3b, 0x0, 0x0, 0x0,
            0xb, 0x17, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x1, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0x40, 0x1,
            0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0xb, 0x14, 0x0, 0x0, 0x0, 0x62, 0x0, 0x0, 0x0, 0x0,
            0x0, 0x0, 0x10, 0x40
        };
        // clang-format on
        JsonBinary json_binary(JsonBinary::TYPE_CODE_ARRAY, StringRef(json_array, sizeof(json_array) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == "[\"a\", {\"a\": [3, null, {\"b\": 4}]}]");
    }

    {
        // clang-format off
        UInt8 json_array[] = {
            0x1, 0x0, 0x0, 0x0, 0x4f, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x14, 0x0, 0x0, 0x0, 0x62, 0x1, 0x0, 0x0,
            0x0, 0x3b, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0x3, 0x14, 0x0, 0x0, 0x0, 0x63, 0x3, 0x0, 0x0, 0x0, 0x27, 0x0,
            0x0, 0x0, 0x9, 0x17, 0x0, 0x0, 0x0, 0xb, 0x1f, 0x0, 0x0, 0x0, 0x4, 0x1, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc0
        };
        // clang-format on
        JsonBinary json_binary(JsonBinary::TYPE_CODE_OBJECT, StringRef(json_array, sizeof(json_array) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == "{\"b\": {\"c\": [3, -3, true]}}");
    }

    {
        // clang-format off
        UInt8 json_array[] = {
            0x1, 0x0, 0x0, 0x0, 0x74, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x14, 0x0, 0x0, 0x0, 0x62, 0x1, 0x0, 0x0,
            0x0, 0x60, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0x3, 0x14, 0x0, 0x0, 0x0, 0x63, 0x3, 0x0, 0x0, 0x0, 0x4c, 0x0,
            0x0, 0x0, 0x9, 0x17, 0x0, 0x0, 0x0, 0xb, 0x1f, 0x0, 0x0, 0x0, 0xc, 0x27, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc0, 0x24, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x22, 0x65, 0x73, 0x63, 0x61,
            0x70, 0x65, 0x64, 0x20, 0xe4, 0xbd, 0xa0, 0xe5, 0xa5, 0xbd, 0x20, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x22, 0x20, 0x77,
            0x6f, 0x72, 0x6c, 0x64
        };
        // clang-format on
        JsonBinary json_binary(JsonBinary::TYPE_CODE_OBJECT, StringRef(json_array, sizeof(json_array) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == "{\"b\": {\"c\": [3, -3, \"hello, \\\"escaped 你好 quotes\\\" world\"]}}");
    }
}
CATCH

TEST_F(TestJsonBinary, TestUnquoteDateAndTime)
try
{
    {
        UInt8 json_date[8] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x7e, 0x98, 0x1f};
        String expected = "2022-01-31";
        JsonBinary json_binary(JsonBinary::TYPE_CODE_DATE, StringRef(json_date, 8));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected);
    }

    {
        UInt8 json_date[] = {0x20, 0x4d, 0x0, 0x14, 0x35, 0x7e, 0x98, 0x1f};
        String expected = "2022-01-31 03:20:20.001234";
        JsonBinary json_binary(JsonBinary::TYPE_CODE_DATETIME, StringRef(json_date, sizeof(json_date) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected);
    }

    {
        UInt8 json_date[] = {0x20, 0x4d, 0x0, 0x14, 0x35, 0xfe, 0x98, 0x1f};
        String expected = "2022-03-31 03:20:20.001234";
        JsonBinary json_binary(
            JsonBinary::TYPE_CODE_TIMESTAMP,
            StringRef(json_date, sizeof(json_date) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected);
    }
}
CATCH

TEST_F(TestJsonBinary, TestUnquoteDuration)
try
{
    {
        UInt8 json_duration[] = {0x50, 0x58, 0xe1, 0x78, 0xf7, 0xa, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0};
        String expected = "03:20:58.001234";
        JsonBinary json_binary(
            JsonBinary::TYPE_CODE_DURATION,
            StringRef(json_duration, sizeof(json_duration) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected);
    }

    {
        UInt8 json_duration[] = {0xb0, 0xa7, 0x1e, 0x87, 0x8, 0xf5, 0xff, 0xff, 0x6, 0x0, 0x0, 0x0};
        String expected = "-03:20:58.001234";
        JsonBinary json_binary(
            JsonBinary::TYPE_CODE_DURATION,
            StringRef(json_duration, sizeof(json_duration) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected);
    }
}
CATCH

TEST_F(TestJsonBinary, TestUnquoteOpaque)
try
{
    {
        UInt8 json_opaque[] = {0xe9, 0x1, 0x39};
        String expected = "base64:type233:OQ==";
        JsonBinary json_binary(
            JsonBinary::TYPE_CODE_OPAQUE,
            StringRef(json_opaque, sizeof(json_opaque) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected);
    }

    {
        // clang-format off
        UInt8 json_opaque[] = {
            0xe9, 0x80, 0x1, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
            0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
            0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
            0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
            0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
            0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39, 0x39,
            0x39, 0x39, 0x39, 0x39, 0x39
        };
        // clang-format on
        String expected = "base64:type233:"
                          "OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5"
                          "OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk=";
        JsonBinary json_binary(
            JsonBinary::TYPE_CODE_OPAQUE,
            StringRef(json_opaque, sizeof(json_opaque) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected);
    }
}
CATCH

TEST_F(TestJsonBinary, TestUnquoteString)
try
{
    {
        UInt8 json_string[] = {0x0};
        String expected;
        JsonBinary json_binary(
            JsonBinary::TYPE_CODE_STRING,
            StringRef(json_string, sizeof(json_string) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected);
    }

    {
        UInt8 json_string[] = {0xa, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x57, 0x6f, 0x72, 0x6c, 0x64};
        String expected = "HelloWorld";
        JsonBinary json_binary(
            JsonBinary::TYPE_CODE_STRING,
            StringRef(json_string, sizeof(json_string) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected);
    }

    {
        UInt8 json_string[] = {0x6, 0xe4, 0xbd, 0xa0, 0xe5, 0xa5, 0xbd};
        String expected = "你好";
        JsonBinary json_binary(
            JsonBinary::TYPE_CODE_STRING,
            StringRef(json_string, sizeof(json_string) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected);
    }

    {
        // clang-format off
        UInt8 json_string[] = {
            0x1d, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x22, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x64, 0x20, 0x71, 0x75, 0x6f,
            0x74, 0x65, 0x73, 0x22, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64
        };
        // clang-format on
        String expected = "hello, \"escaped quotes\" world";
        JsonBinary json_binary(
            JsonBinary::TYPE_CODE_STRING,
            StringRef(json_string, sizeof(json_string) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected);
    }

    {
        // clang-format off
        UInt8 json_string[] = {
            0x24, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x22, 0x65, 0x73, 0x63, 0x61, 0x70, 0x65, 0x64, 0x20, 0xe4, 0xbd, 0xa0,
            0xe5, 0xa5, 0xbd, 0x20, 0x71, 0x75, 0x6f, 0x74, 0x65, 0x73, 0x22, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64
        };
        // clang-format on
        String expected = "hello, \"escaped 你好 quotes\" world";
        JsonBinary json_binary(
            JsonBinary::TYPE_CODE_STRING,
            StringRef(json_string, sizeof(json_string) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected);
    }

    {
        UInt8 json_string[] = {0x1, 0x27};
        String expected = "'";
        JsonBinary json_binary(
            JsonBinary::TYPE_CODE_STRING,
            StringRef(json_string, sizeof(json_string) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected);
    }

    {
        UInt8 json_string[] = {0x2, 0x27, 0x27};
        String expected = "''";
        JsonBinary json_binary(
            JsonBinary::TYPE_CODE_STRING,
            StringRef(json_string, sizeof(json_string) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected);
    }

    {
        UInt8 json_string[] = {0x4, 0x61, 0xa, 0x62, 0x5c};
        String expected = "a\nb\\";
        JsonBinary json_binary(
            JsonBinary::TYPE_CODE_STRING,
            StringRef(json_string, sizeof(json_string) / sizeof(UInt8)));
        auto str = JsonBinary::unquoteString(json_binary.toString());
        ASSERT_TRUE(str == expected);
    }
}
CATCH

TEST_F(TestJsonBinary, TestExtract)
try
{
    // clang-format off
    /// `{"\"hello\"": "world", "a": [1, "2", {"aa": "bb"}, 4.0, {"aa": "cc"}], "b": true, "c": ["d"]}`
    UInt8 bj1[] = {
        0x4, 0x0, 0x0, 0x0, 0xb6, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x7, 0x0, 0x3b, 0x0, 0x0, 0x0, 0x1, 0x0, 0x3c, 0x0, 0x0,
        0x0, 0x1, 0x0, 0x3d, 0x0, 0x0, 0x0, 0x1, 0x0, 0xc, 0x3e, 0x0, 0x0, 0x0, 0x3, 0x44, 0x0, 0x0, 0x0, 0x4, 0x1, 0x0, 0x0, 0x0,
        0x3, 0xa7, 0x0, 0x0, 0x0, 0x22, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x22, 0x61, 0x62, 0x63, 0x5, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x5,
        0x0, 0x0, 0x0, 0x63, 0x0, 0x0, 0x0, 0x9, 0x21, 0x0, 0x0, 0x0, 0xc, 0x29, 0x0, 0x0, 0x0, 0x1, 0x2b, 0x0, 0x0, 0x0, 0xb, 0x43,
        0x0, 0x0, 0x0, 0x1, 0x4b, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x32, 0x1, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0,
        0x0, 0x13, 0x0, 0x0, 0x0, 0x2, 0x0, 0xc, 0x15, 0x0, 0x0, 0x0, 0x61, 0x61, 0x2, 0x62, 0x62, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10,
        0x40, 0x1, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x2, 0x0, 0xc, 0x15, 0x0, 0x0, 0x0, 0x61, 0x61, 0x2, 0x63,
        0x63, 0x1, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0xc, 0xd, 0x0, 0x0, 0x0, 0x1, 0x64
    };
    JsonBinary json_binary_1(JsonBinary::TYPE_CODE_OBJECT, StringRef(bj1, sizeof(bj1) / sizeof(UInt8)));

    /// `[{"a": 1, "b": true}, 3, 3.5, "hello, world", null, true]`
    UInt8 bj2[] = {
        0x6, 0x0, 0x0, 0x0, 0x6b, 0x0, 0x0, 0x0, 0x1, 0x26, 0x0, 0x0, 0x0, 0x9, 0x4e, 0x0, 0x0, 0x0, 0xb, 0x56, 0x0, 0x0, 0x0, 0xc, 0x5e,
        0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x4, 0x1, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x1,
        0x0, 0x1f, 0x0, 0x0, 0x0, 0x1, 0x0, 0x9, 0x20, 0x0, 0x0, 0x0, 0x4, 0x1, 0x0, 0x0, 0x0, 0x61, 0x62, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc, 0x40, 0xc, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77,
        0x6f, 0x72, 0x6c, 0x64
    };
    JsonBinary json_binary_2(JsonBinary::TYPE_CODE_ARRAY, StringRef(bj2, sizeof(bj2) / sizeof(UInt8)));

    /// `{"properties": {"$type": "TiDB"}}`
    UInt8 bj3[] = {
        0x1, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0xa, 0x0, 0x1, 0x1d, 0x0, 0x0, 0x0, 0x70, 0x72, 0x6f, 0x70, 0x65,
        0x72, 0x74, 0x69, 0x65, 0x73, 0x1, 0x0, 0x0, 0x0, 0x1d, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x5, 0x0, 0xc, 0x18, 0x0, 0x0, 0x0,
        0x24, 0x74, 0x79, 0x70, 0x65, 0x4, 0x54, 0x69, 0x44, 0x42
    };
    JsonBinary json_binary_3(JsonBinary::TYPE_CODE_OBJECT, StringRef(bj3, sizeof(bj3) / sizeof(UInt8)));

    /// `{"properties": {"$type$type": {"$a$a" : "TiDB"}}}`
    UInt8 bj4[] = {
        0x1, 0x0, 0x0, 0x0, 0x56, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0xa, 0x0, 0x1, 0x1d, 0x0, 0x0, 0x0, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x1, 0x0, 0x0, 0x0,
        0x39, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0xa, 0x0, 0x1, 0x1d, 0x0, 0x0, 0x0, 0x24, 0x74, 0x79, 0x70, 0x65, 0x24, 0x74, 0x79, 0x70,
        0x65, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x4, 0x0, 0xc, 0x17, 0x0, 0x0, 0x0, 0x24, 0x61, 0x24, 0x61, 0x4,
        0x54, 0x69, 0x44, 0x42
    };
    JsonBinary json_binary_4(JsonBinary::TYPE_CODE_OBJECT, StringRef(bj4, sizeof(bj4) / sizeof(UInt8)));

    /// `{"properties": {"$type": {"$a" : {"$b" : "TiDB"}}}}`
    UInt8 bj5[] = {
        0x1, 0x0, 0x0, 0x0, 0x64, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0xa, 0x0, 0x1, 0x1d, 0x0, 0x0, 0x0, 0x70, 0x72, 0x6f, 0x70, 0x65,
        0x72, 0x74, 0x69, 0x65, 0x73, 0x1, 0x0, 0x0, 0x0, 0x47, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x5, 0x0, 0x1, 0x18, 0x0, 0x0, 0x0, 0x24,
        0x74, 0x79, 0x70, 0x65, 0x1, 0x0, 0x0, 0x0, 0x2f, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1, 0x15, 0x0, 0x0, 0x0, 0x24, 0x61,
        0x1, 0x0, 0x0, 0x0, 0x1a, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x2, 0x0, 0xc, 0x15, 0x0, 0x0, 0x0, 0x24, 0x62, 0x4, 0x54, 0x69, 0x44, 0x42
    };
    JsonBinary json_binary_5(JsonBinary::TYPE_CODE_OBJECT, StringRef(bj5, sizeof(bj5) / sizeof(UInt8)));

    /// `{"properties": {"$type": {"$a$a" : "TiDB"}},"hello": {"$b$b": "world","$c": "amazing"}}`
    UInt8 bj6[] = {
        0x2, 0x0, 0x0, 0x0, 0x93, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x5, 0x0, 0x23, 0x0, 0x0, 0x0, 0xa, 0x0, 0x1, 0x2d, 0x0, 0x0, 0x0, 0x1,
        0x5f, 0x0, 0x0, 0x0, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x70, 0x72, 0x6f, 0x70, 0x65, 0x72, 0x74, 0x69, 0x65, 0x73, 0x2, 0x0, 0x0, 0x0, 0x32,
        0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x4, 0x0, 0x22, 0x0, 0x0, 0x0, 0x2, 0x0, 0xc, 0x24, 0x0, 0x0, 0x0, 0xc, 0x2a, 0x0, 0x0, 0x0, 0x24, 0x62,
        0x24, 0x62, 0x24, 0x63, 0x5, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x7, 0x61, 0x6d, 0x61, 0x7a, 0x69, 0x6e, 0x67, 0x1, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0,
        0x0, 0x13, 0x0, 0x0, 0x0, 0x5, 0x0, 0x1, 0x18, 0x0, 0x0, 0x0, 0x24, 0x74, 0x79, 0x70, 0x65, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x13, 0x0,
        0x0, 0x0, 0x4, 0x0, 0xc, 0x17, 0x0, 0x0, 0x0, 0x24, 0x61, 0x24, 0x61, 0x4, 0x54, 0x69, 0x44, 0x42
    };
    JsonBinary json_binary_6(JsonBinary::TYPE_CODE_OBJECT, StringRef(bj6, sizeof(bj6) / sizeof(UInt8)));

    /// `{ "a": { "x" : { "b": { "y": { "b": { "z": { "c": 100 } } } } } } }`
    UInt8 bj7[] = {
        0x1, 0x0, 0x0, 0x0, 0x94, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x14, 0x0, 0x0, 0x0, 0x61, 0x1, 0x0, 0x0, 0x0, 0x80, 0x0,
        0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x14, 0x0, 0x0, 0x0, 0x78, 0x1, 0x0, 0x0, 0x0, 0x6c, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0,
        0x1, 0x0, 0x1, 0x14, 0x0, 0x0, 0x0, 0x62, 0x1, 0x0, 0x0, 0x0, 0x58, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x14, 0x0, 0x0,
        0x0, 0x79, 0x1, 0x0, 0x0, 0x0, 0x44, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x14, 0x0, 0x0, 0x0, 0x62, 0x1, 0x0, 0x0, 0x0,
        0x30, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x14, 0x0, 0x0, 0x0, 0x7a, 0x1, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x0, 0x13, 0x0,
        0x0, 0x0, 0x1, 0x0, 0x9, 0x14, 0x0, 0x0, 0x0, 0x63, 0x64, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
    };
    JsonBinary json_binary_7(JsonBinary::TYPE_CODE_OBJECT, StringRef(bj7, sizeof(bj7) / sizeof(UInt8)));

    /// `{ "a": { "b" : [ 1, 2, 3 ] } }`
    UInt8 bj8[] = {
        0x1, 0x0, 0x0, 0x0, 0x57, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x14, 0x0, 0x0, 0x0, 0x61, 0x1, 0x0, 0x0, 0x0, 0x43, 0x0,
        0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0x3, 0x14, 0x0, 0x0, 0x0, 0x62, 0x3, 0x0, 0x0, 0x0, 0x2f, 0x0, 0x0, 0x0, 0x9, 0x17, 0x0, 0x0,
        0x0, 0x9, 0x1f, 0x0, 0x0, 0x0, 0x9, 0x27, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
        0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
    };
    JsonBinary json_binary_8(JsonBinary::TYPE_CODE_OBJECT, StringRef(bj8, sizeof(bj8) / sizeof(UInt8)));

    /// `[[0,1],[2,3],[4,[5,6]]]`
    UInt8 bj9[] = {
        0x3, 0x0, 0x0, 0x0, 0x97, 0x0, 0x0, 0x0, 0x3, 0x17, 0x0, 0x0, 0x0, 0x3, 0x39, 0x0, 0x0, 0x0, 0x3, 0x5b, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0,
        0x22, 0x0, 0x0, 0x0, 0x9, 0x12, 0x0, 0x0, 0x0, 0x9, 0x1a, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0,
        0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x9, 0x12, 0x0, 0x0, 0x0, 0x9, 0x1a, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0,
        0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x9, 0x12, 0x0, 0x0, 0x0, 0x3, 0x1a, 0x0, 0x0,
        0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x9, 0x12, 0x0, 0x0, 0x0, 0x9, 0x1a, 0x0, 0x0, 0x0,
        0x5, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
    };
    JsonBinary json_binary_9(JsonBinary::TYPE_CODE_ARRAY, StringRef(bj9, sizeof(bj9) / sizeof(UInt8)));

    /// `[1]`
    UInt8 bj10[] = {0x1, 0x0, 0x0, 0x0, 0x15, 0x0, 0x0, 0x0, 0x9, 0xd, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
    JsonBinary json_binary_10(JsonBinary::TYPE_CODE_ARRAY, StringRef(bj10, sizeof(bj10) / sizeof(UInt8)));

    /// `{"metadata": {"comment": "1234"}}`
    UInt8 bj11[] = {
        0x1, 0x0, 0x0, 0x0, 0x3a, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x8, 0x0, 0x1, 0x1b, 0x0, 0x0, 0x0, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
        0x61, 0x1, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x7, 0x0, 0xc, 0x1a, 0x0, 0x0, 0x0, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e,
        0x74, 0x4, 0x31, 0x32, 0x33, 0x34
    };
    JsonBinary json_binary_11(JsonBinary::TYPE_CODE_OBJECT, StringRef(bj11, sizeof(bj11) / sizeof(UInt8)));

    /// `{"metadata": {"age": 19, "name": "Tom"}}`
    UInt8 bj12[] = {
        0x1, 0x0, 0x0, 0x0, 0x4c, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x8, 0x0, 0x1, 0x1b, 0x0, 0x0, 0x0, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
        0x61, 0x2, 0x0, 0x0, 0x0, 0x31, 0x0, 0x0, 0x0, 0x1e, 0x0, 0x0, 0x0, 0x3, 0x0, 0x21, 0x0, 0x0, 0x0, 0x4, 0x0, 0x9, 0x25, 0x0, 0x0, 0x0, 0xc,
        0x2d, 0x0, 0x0, 0x0, 0x61, 0x67, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x13, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x54, 0x6f, 0x6d
    };
    JsonBinary json_binary_12(JsonBinary::TYPE_CODE_OBJECT, StringRef(bj12, sizeof(bj12) / sizeof(UInt8)));

    UInt8 bj13[] = {0x1, 0x0, 0x0, 0x0, 0x4f, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x14, 0x0, 0x0, 0x0, 0x62, 0x1, 0x0, 0x0, 0x0, 0x3b, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x6, 0x0, 0x3, 0x19, 0x0, 0x0, 0x0, 0xe4, 0xbd, 0xa0, 0xe5, 0xa5, 0xbd, 0x2, 0x0, 0x0, 0x0, 0x22, 0x0, 0x0, 0x0, 0x9, 0x12, 0x0, 0x0, 0x0, 0xb, 0x1a, 0x0, 0x0, 0x0, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x8, 0xc0};
    JsonBinary json_binary_13(JsonBinary::TYPE_CODE_OBJECT, StringRef(bj13, sizeof(bj13) / sizeof(UInt8)));
    // clang-format on
    struct TestData
    {
        JsonBinary bj;
        std::vector<String> path_expr_vec;
        bool found;
        String expected_result;
    };

    std::vector<TestData> test_data_vec{
        {json_binary_3, {"$"}, true, R"({"properties": {"$type": "TiDB"}})"},
        {json_binary_1, {"$.a"}, true, R"([1, "2", {"aa": "bb"}, 4, {"aa": "cc"}])"},
        {json_binary_2, {"$.a"}, false, ""},
        {json_binary_1,
         {"$[0]"},
         true,
         R"({"\"hello\"": "world", "a": [1, "2", {"aa": "bb"}, 4, {"aa": "cc"}], "b": true, "c": ["d"]})"},
        {json_binary_2, {"$[0]"}, true, R"({"a": 1, "b": true})"},
        {json_binary_1, {"$.a[2].aa"}, true, "bb"},
        {json_binary_1, {"$.a[*].aa"}, true, R"(["bb", "cc"])"},
        {json_binary_1, {"$.*[0]"}, true, R"(["world", 1, true, "d"])"},
        {json_binary_1, {"$.a[*].\"aa\""}, true, R"(["bb", "cc"])"},
        {json_binary_1, {R"($."\"hello\"")"}, true, "world"},
        {json_binary_1, {"$**[1]"}, true, "[\"2\"]"},
        {json_binary_3, {"$.properties.$type"}, true, "TiDB"},
        {json_binary_4, {"$.properties.$type$type"}, true, R"({"$a$a": "TiDB"})"},
        {json_binary_4, {"$.properties.$type$type.$a$a"}, true, "TiDB"},
        {json_binary_5, {"$.properties.$type.$a.$b"}, true, "TiDB"},
        {json_binary_5, {"$.properties.$type.$a.*[0]"}, true, "[\"TiDB\"]"},
        {json_binary_11, {"$.metadata.comment"}, true, "1234"},
        {json_binary_13, {"$.b.你好"}, true, "[3, -3]"},
        {json_binary_9, {"$[0]"}, true, "[0, 1]"},
        {json_binary_9, {"$[last][last]"}, true, "[5, 6]"},
        {json_binary_9, {"$[last-1][last]"}, true, "3"},
        {json_binary_9, {"$[last-1][last-1]"}, true, "2"},
        {json_binary_9, {"$[1 to 2]"}, true, "[[2, 3], [4, [5, 6]]]"},
        {json_binary_1, {"$.a", "$[5]"}, true, R"([[1, "2", {"aa": "bb"}, 4, {"aa": "cc"}]])"},
        {json_binary_2, {"$.a", "$[0]"}, true, R"([{"a": 1, "b": true}])"},
        {json_binary_6, {"$.properties", "$[1]"}, true, R"([{"$type": {"$a$a": "TiDB"}}])"},
        {json_binary_6, {"$.hello", "$[2]"}, true, R"([{"$b$b": "world", "$c": "amazing"}])"},
        {json_binary_7, {"$.a**.b**.c"}, true, "[100]"},
        {json_binary_8, {"$**[0]"}, true, R"([{"a": {"b": [1, 2, 3]}}, {"b": [1, 2, 3]}, 1, 2, 3])"},
        {json_binary_9, {"$**[0]"}, true, "[[0, 1], 0, 1, 2, 3, 4, 5, 6]"},
        {json_binary_10, {"$**[0]"}, true, "[1]"},
        {json_binary_12, {"$.metadata.age", "$.metadata.name"}, true, "[19, \"Tom\"]"},
    };

    for (auto & i : test_data_vec)
    {
        std::vector<JsonPathExprRefContainerPtr> path_expr_container_vec;
        for (auto & j : i.path_expr_vec)
        {
            auto path_expr = JsonPathExpr::parseJsonPathExpr(j);
            ASSERT_TRUE(path_expr);
            path_expr_container_vec.push_back(std::make_unique<JsonPathExprRefContainer>(path_expr));
        }

        /// Test twice to validate cache functionality
        for (size_t j = 0; j < 2; ++j)
        {
            auto col_to = ColumnString::create();
            ColumnString::Chars_t & data_to = col_to->getChars();
            ColumnString::Offsets & offsets_to = col_to->getOffsets();
            JsonBinary::JsonBinaryWriteBuffer write_buffer(data_to);
            bool found = i.bj.extract(path_expr_container_vec, write_buffer);
            ASSERT_TRUE(found == i.found);
            if (found)
            {
                writeChar(0, write_buffer);
                offsets_to.push_back(write_buffer.count());
                data_to.resize(write_buffer.count());
                JsonBinary result(data_to[0], StringRef(col_to->getDataAt(0).data + 1, col_to->getDataAt(0).size - 1));
                ASSERT_TRUE(i.expected_result == JsonBinary::unquoteString(result.toString()));
            }
        }
    }
}
CATCH


TEST_F(TestJsonBinary, TestExtractKeyCache)
try
{
    // clang-format off
    /// `{"\"hello\"": "world", "a": [1, "2", {"aa": "bb"}, 4.0, {"aa": "cc"}], "b": true, "c": ["d"]}`
    UInt8 bj1[] = {
        0x4, 0x0, 0x0, 0x0, 0xb6, 0x0, 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x7, 0x0, 0x3b, 0x0, 0x0, 0x0, 0x1, 0x0, 0x3c, 0x0, 0x0,
        0x0, 0x1, 0x0, 0x3d, 0x0, 0x0, 0x0, 0x1, 0x0, 0xc, 0x3e, 0x0, 0x0, 0x0, 0x3, 0x44, 0x0, 0x0, 0x0, 0x4, 0x1, 0x0, 0x0, 0x0,
        0x3, 0xa7, 0x0, 0x0, 0x0, 0x22, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x22, 0x61, 0x62, 0x63, 0x5, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x5,
        0x0, 0x0, 0x0, 0x63, 0x0, 0x0, 0x0, 0x9, 0x21, 0x0, 0x0, 0x0, 0xc, 0x29, 0x0, 0x0, 0x0, 0x1, 0x2b, 0x0, 0x0, 0x0, 0xb, 0x43,
        0x0, 0x0, 0x0, 0x1, 0x4b, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x32, 0x1, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0,
        0x0, 0x13, 0x0, 0x0, 0x0, 0x2, 0x0, 0xc, 0x15, 0x0, 0x0, 0x0, 0x61, 0x61, 0x2, 0x62, 0x62, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x10,
        0x40, 0x1, 0x0, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0, 0x13, 0x0, 0x0, 0x0, 0x2, 0x0, 0xc, 0x15, 0x0, 0x0, 0x0, 0x61, 0x61, 0x2, 0x63,
        0x63, 0x1, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0, 0xc, 0xd, 0x0, 0x0, 0x0, 0x1, 0x64
    };
    // clang-format on
    JsonBinary json_binary_1(JsonBinary::TYPE_CODE_OBJECT, StringRef(bj1, sizeof(bj1) / sizeof(UInt8)));

    auto col_to = ColumnString::create();
    ColumnString::Chars_t & data_to = col_to->getChars();
    JsonBinary::JsonBinaryWriteBuffer write_buffer(data_to);
    {
        /// Simple cache success check
        std::vector<JsonPathExprRefContainerPtr> path_expr_container_vec;
        auto path_expr = JsonPathExpr::parseJsonPathExpr("$.a");
        ASSERT_TRUE(path_expr);
        path_expr_container_vec.push_back(std::make_unique<JsonPathExprRefContainer>(path_expr));
        checkJsonPathKeyUncached(path_expr);

        bool found = json_binary_1.extract(path_expr_container_vec, write_buffer);
        ASSERT_TRUE(
            found && path_expr->getLegs()[0]->dot_key.status == JsonPathObjectKeyCached
            && path_expr->getLegs()[0]->dot_key.cached_index == 1);
    }

    {
        /// Simple cache success check 2
        std::vector<JsonPathExprRefContainerPtr> path_expr_container_vec;
        auto path_expr = JsonPathExpr::parseJsonPathExpr("$.a[2].aa");
        ASSERT_TRUE(path_expr);
        path_expr_container_vec.push_back(std::make_unique<JsonPathExprRefContainer>(path_expr));
        checkJsonPathKeyUncached(path_expr);

        bool found = json_binary_1.extract(path_expr_container_vec, write_buffer);
        ASSERT_TRUE(found);
        ASSERT_TRUE(
            path_expr->getLegs()[0]->dot_key.status == JsonPathObjectKeyCached
            && path_expr->getLegs()[0]->dot_key.cached_index == 1);
        ASSERT_TRUE(
            path_expr->getLegs()[2]->dot_key.status == JsonPathObjectKeyCached
            && path_expr->getLegs()[2]->dot_key.cached_index == 0);

        /// Extract once more
        found = json_binary_1.extract(path_expr_container_vec, write_buffer);
        ASSERT_TRUE(found);
        ASSERT_TRUE(
            path_expr->getLegs()[0]->dot_key.status == JsonPathObjectKeyCached
            && path_expr->getLegs()[0]->dot_key.cached_index == 1);
        ASSERT_TRUE(
            path_expr->getLegs()[2]->dot_key.status == JsonPathObjectKeyCached
            && path_expr->getLegs()[2]->dot_key.cached_index == 0);
    }

    {
        /// Un-cacheable case 1: Asterisk-star for key
        std::vector<JsonPathExprRefContainerPtr> path_expr_container_vec;
        auto path_expr = JsonPathExpr::parseJsonPathExpr("$.*[0]");
        ASSERT_TRUE(path_expr);
        path_expr_container_vec.push_back(std::make_unique<JsonPathExprRefContainer>(path_expr));
        checkJsonPathKeyCacheDisable(path_expr);

        bool found = json_binary_1.extract(path_expr_container_vec, write_buffer);
        ASSERT_TRUE(found);
        checkJsonPathKeyCacheDisable(path_expr);
    }

    {
        /// Un-cacheable case 2: Asterisk-star for array index
        std::vector<JsonPathExprRefContainerPtr> path_expr_container_vec;
        auto path_expr = JsonPathExpr::parseJsonPathExpr("$.a[*].aa");
        ASSERT_TRUE(path_expr);
        path_expr_container_vec.push_back(std::make_unique<JsonPathExprRefContainer>(path_expr));
        checkJsonPathKeyCacheDisable(path_expr);

        bool found = json_binary_1.extract(path_expr_container_vec, write_buffer);
        ASSERT_TRUE(found);
        checkJsonPathKeyCacheDisable(path_expr);
    }

    {
        /// Un-cacheable case 3: Double Asterisk-star
        std::vector<JsonPathExprRefContainerPtr> path_expr_container_vec;
        auto path_expr = JsonPathExpr::parseJsonPathExpr("$**[2].aa");
        ASSERT_TRUE(path_expr);
        path_expr_container_vec.push_back(std::make_unique<JsonPathExprRefContainer>(path_expr));
        checkJsonPathKeyCacheDisable(path_expr);

        bool found = json_binary_1.extract(path_expr_container_vec, write_buffer);
        ASSERT_TRUE(found);
        checkJsonPathKeyCacheDisable(path_expr);
    }
}
CATCH

TEST_F(TestJsonBinary, TestConstant)
try
{
    for (size_t i = 0; i < 32; ++i)
    {
        ASSERT_TRUE(!JsonSafeAscii[i]);
    }

    for (size_t i = 32; i < 128; ++i)
    {
        if (i == '"' || i == '\\')
            ASSERT_TRUE(!JsonSafeAscii[i]);
        else
            ASSERT_TRUE(JsonSafeAscii[i]);
    }
}
CATCH

} // namespace tests
} // namespace DB